// 类型操作: 通过 TS 的类型操作符，从已有的类型中创建新的类型！
// 在 TS 中，类型操作符：typeof、keyof、in、extends、infer 五个类型操作符

// 1. typeof 类型操作符: 从已经存在的对象中，获取它的类型
let str = "hello TypeScript";
type strType = typeof str;

const p1 = {
  name: "zs",
  age: 18,
  sex: "male",
};

type PersonType = typeof p1;

function add(x: number, y: number) {
  return x + y;
}

type AddType = typeof add;

// 2. keyof 类型操作符: 获取某个类型的所有键，并以联合类型形式返回
type PersonKeys = keyof PersonType;

function getValue<T extends object, K extends keyof T>(p: T, key: K) {
  return p[key];
}
const point1 = {
  x: 1,
  y: 2,
  distance(other: { x: number; y: number }) {
    return Math.sqrt(
      Math.pow(this.x - other.x, 2) + Math.pow(this.y - other.y, 2)
    );
  },
};
getValue(p1, "name");
getValue(point1, "x");

// 3. in 类型操作符: in 操作符用于遍历目标类型的属性key值。
// 类似 for .. in 的机制。一般结合 [] 索引签名一起使用。
// in 操作符可以用来遍历 枚举、联合类型、基础类型（string, number, symbol）
type Property = "name" | "age" | "phoneNum";

type PropertyMap = {
  [key in Property]: string;
};

// 4. extends 类型操作符: extends 操作符结合泛型使用时，可以用作类型范围限制。
// 例如前面讨论keyof操作符，声明的getProp函数。
// 这里限制了K的取值范围必须是keyof T，也即类型T的key值的联合类型或者其子集
function plus<T extends string | number>(x: T, y: T) {
  return (x as any) + y;
}

plus(1, 2);
plus("hello", " world");

type PickSome<T, K extends keyof T> = {
  [P in K]: T[P];
};

type Person = {
  name: string;
  age: number;
  sex: boolean;
};
type Other = PickSome<Person, "name" | "age">;

// T extends U ? X : Y 条件表达操作
type CanPlus<T = any> = T extends string ? string : number;
function plus2<T = CanPlus>(x: T, y: T) {
  return (x as any) + y;
}

// 5. infer: infer操作符可以用来声明一个待推断的类型;
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;

type AddReturnType = ReturnType<typeof add>;

// 条件类型表达式中，且 infer 只能结合 extends 在条件类型中使用。
type Unpacked<T> = T extends (infer U)[]
  ? U
  : T extends (...args: any[]) => infer U
  ? U
  : T extends Promise<T>
  ? T
  : any;

type T1 = Unpacked<string[]>;
type T2 = Unpacked<() => string>;

type ArrayType<T extends any[]> = T extends (infer U)[] ? U : any;
type PromiseType<T extends Promise<any>> = T extends Promise<infer U> ? U : any;

type T3 = ArrayType<string[]>;
type T4 = PromiseType<Promise<string>>;

// TS 内置了一下用于操作类型的工具类型
// Partial<T>: 将T的所有属性变为可选
type PartialPerson = Partial<Person>;
// Required<T>: 将T的所有属性变为必选
type RequiredPerson = Required<PartialPerson>;
// Readonly<T>: 将T的所有属性变为只读
type ReadonlyPerson = Readonly<Person>;
// Pick<T, K>: 从T中选取K的属性
type PickPerson = Pick<Person, "name" | "age">;
// Omit<T, K>: 从T中排除K的属性
type OmitPerson = Omit<Person, "name">;
// Exclude<T, U>: 从 T 中排除 U，该操作类型只能使用在联合类型中
type ExcludePerson = Exclude<"a" | "b" | "c", "a" | "b">;
